/* File: EatStrategy.java
 * Purpose: Defines an organism's algorithm for consuming food
 * Author: Team Karp
 * Date: 12/13/07
 * Revisions: 12/13/07, Adam Patterson - Initial Version
 *           [2008-01-22, Adam Patterson - Implemented scripting function]
 * Additional Requirements: 
 */
package protovitae.data;

import javax.script.*;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

/**
 * @version 1.0
 * @created 06-Dec-2007 16:19:09
 */
public class EatStrategy  extends GeneActionStrategy {

    public EatStrategy() {
        StringBuilder builder = new StringBuilder();  
         builder.append("if(!food.checkLife()){\n");
             builder.append("var healthm = parent.getAttribute(parent.getAttributeOrdinalFromString(\"HEALTH\")).getMax();\n");
             builder.append("var health = parent.getAttribute(parent.getAttributeOrdinalFromString(\"HEALTH\")).getCurrent();\n");
             builder.append("var foodDef = food.getAttribute(food.getAttributeOrdinalFromString(\"FORTITUDE\")).getCurrent() * 2;\n");
             builder.append("var foodPile = food.getAttribute(parent.getAttributeOrdinalFromString(\"HEALTH\")).getCurrent();\n");
             builder.append("var energy = parent.getAttribute(parent.getAttributeOrdinalFromString(\"ACTION\")).getCurrent();\n");
             builder.append("var attack = (parent.getAttribute(parent.getAttributeOrdinalFromString(\"BRAWN\")).getCurrent() * parent.getAttribute(parent.getAttributeOrdinalFromString(\"BRAWN\")).getCurrent() * 2) % healthm -foodDef;\n");
             builder.append("if(energy >3 && attack>0){\n");
                     builder.append("food.getAttribute(parent.getAttributeOrdinalFromString(\"HEALTH\")).setCurrent(foodPile-attack);\n");
                     builder.append("parent.getAttribute(parent.getAttributeOrdinalFromString(\"ACTION\")).setCurrent(energy-3);\n");
                     builder.append("parent.getAttribute(parent.getAttributeOrdinalFromString(\"HEALTH\")).setCurrent(health+attack);\n");
             builder.append("}\n");
         builder.append("}\n");
         
        try {
            parse(builder.toString()); //Set code
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * This method is called to execute the GeneActionStrategy.
     * @param information - the food source (ConcreteOrganism)
     * @param parent - owning ConcreteOrgansim
     * @return the modified (ConcreteOrganism) food object
     */
    public Object execute(Object information, ConcreteOrganism parent) {
        
        if (actionScript != null) {
            try {
                information = actionScript.invokeFunction("eat", (ConcreteOrganism) information, parent);
            } catch (NoSuchMethodException nsme) {
                nsme.printStackTrace();
            } catch (ScriptException se) {
                se.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace(); //There should be a catch-all, in case something we don't initially anticipate occurs
            }
        }

        //if the food is gone, set it to null
        //This is okay for now but we need some sort of dispose method to remove the organism from the field, as well
        //Maybe the same function that we use in our DeleteOrganism button, since that'll be what happens to it
//        if (((ConcreteOrganism) information).getAttribute(Organism.ATTRIBUTE_TYPE.HEALTH).getCurrent() == 0) {
//            information = null;
//        }

        //return modified food source
        //As mentioned above, this is alright for now but it might be better to change it to a different type, like a boolean so that an organism can remove itself or something like that
        return information;
    }

    /**
     * 
     * @param parent
     */
    public void save(Element parentElm, Document doc) {
        Element eat = doc.createElement("eat");
        
        Element xmlcode = doc.createElement("code");
        xmlcode.appendChild(doc.createTextNode(code));
        eat.appendChild(xmlcode);
        
        //Write genetic byte array
        Element gene = doc.createElement("genedata");
        for(Byte thebyte : geneticInformation){
            Element belm = doc.createElement("b");
            gene.appendChild(doc.createTextNode(thebyte.toString()));
            gene.appendChild(belm);
        }
        eat.appendChild(gene);
        
        parentElm.appendChild(eat);
    }

    public void load(Element input) {
        try{
            NodeList myroot = input.getElementsByTagName("eat");
            NodeList mycode = ((Element)myroot.item(0)).getElementsByTagName("code");
            NodeList mydata = ((Element)myroot.item(0)).getElementsByTagName("genedata");
            
            parse(mycode.item(0).getTextContent()); //Calling parse will throw an exception for us if bad code. This is good.
            for(int i=0; i<mydata.getLength(); i++){
                if(mydata.item(i).getNodeName().equals("b"))
                   geneticInformation.add(new Byte(Byte.parseByte(mydata.item(i).getTextContent())));
            }
        }
        catch(Exception e){
            e.printStackTrace(); //A bug occured
        }
    }
    
    /**
     * This parses the code string and stores it in param code.
     * @param code
     * @throws javax.script.ScriptException
     */
    public void parse(String code) throws ScriptException {
        ScriptEngineManager mgr = new ScriptEngineManager();
        ScriptEngine jsEngine = mgr.getEngineByName("JavaScript");

        //load the code into a function with parameters to use
        String scriptedFunctionality = "";
        scriptedFunctionality += "function eat(food,parent) {\n";
        scriptedFunctionality += code;
        scriptedFunctionality += "return food;\n";    //forces return of modified food source
        scriptedFunctionality += "}";

        jsEngine.eval(scriptedFunctionality);
        actionScript = (Invocable) jsEngine;
        this.code = code; //Remember to set this!
    }
}
